<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <input type="button" value="函数抖动" id="debounce">
  <input type="button" value="函数节流" id="throttle">

  <script>
    /*
      1. 事件频繁触发可能造成的问题？
        1). 一些浏览器的时间： window.onresize window.onmousemove等，触发的频率非常高，会造成浏览器的性能问题
        2). 如何向后台发送请求，频繁触发，对服务器造成不必要的压力
      2. 如何限制事件处理函数频繁调用
        1). 函数节流
        2). 函数防抖
      3. 函数节流（throttle）
        1). 理解：
            在函数需要频繁触发时：函数执行一次后，只有大于设定的执行周期后才会执行第二次
        2). 场景:
            窗口调整
            页面滚动
            DOM 元素的拖拽功能实现
            抢购疯狂点击
      4. 函数防抖（debounce）
        1). 理解
          在函数需要频繁触发时: 在规定时间内，只让最后一次生效，前面的不生效
        2). 场景
          实时搜索联想（keyup）
          文本输入的验证（连续输入文字后发送ajax请求进行验证）
    */
    let throttle = document.getElementById('throttle');
    let debounce = document.getElementById('debounce');

    // 事件处理函数
    function handleClick(...args) {
      console.log(this);
      console.log(args);
    }


    // 函数节流
    function throttleFunc(callback, delay, ...params) {
      let start = 0;
      console.log('pre params = ', params)
      return function (...args) {
        console.log('throttle args = ',args)
        // 获取当前的时间值
        let current = Date.now();
        console.log("throttle...");
        // 如果当前的时间值减去上次触发事件的时间大于 delay 那么就再次触发
        if (current - start > delay) {
          start = current;
          // 调用事件处理函数
          
          callback.apply(this, args.concat(params))
        }
      }
    }
    throttle.onclick = throttleFunc(handleClick, 1000, '节流');



    // 函数防抖： 写法1
    function debounceFunc(callback, delay) {
      let timeoutId = null;

      return function (...args) {
        // 如果上次有了，就清除
        if (timeoutId) {
          clearTimeout(timeoutId);
        }

        // 开启延迟
        timeoutId = setTimeout(() => {
          // 调用事件处理函数
          callback.apply(this, args);
        }, delay);

      }
    }

    // 函数防抖： 写法2
    function test(fn, delay) {
      // 维护一个 timer
      let timer = null;

      return function () {
        // 通过 ‘this’ 和 ‘arguments’ 获取函数的作用域和变量
        let context = this;
        let args = arguments;

        clearTimeout(timer);
        timer = setTimeout(function () {
          fn.apply(context, args);
        }, delay);
      }
    }

     /* 函数防抖： 写法3
        更进一步，我们不希望非要等到事件停止触发后才执行，我希望立刻执行函数，然后等到停止触发 n 秒后，才可以重新触发执行。 
     这里增加一个immediate参数来设置是否要立即执行： 
     */
    function test2(func, delay, immediate) {
      var timer = null;
      return function () {
        var context = this;
        var args = arguments;
        if (timer) clearTimeout(timer);
        if (immediate) {
          //根据距离上次触发操作的时间是否到达delay来决定是否要现在执行函数
          var doNow = !timer;
          //每一次都重新设置timer，就是要保证每一次执行的至少delay秒后才可以执行
          timer = setTimeout(function () {
            timer = null;
          }, delay);
          //立即执行
          if (doNow) {
            func.apply(context, args);
          }
        } else {
          timer = setTimeout(function () {
            func.apply(context, args);
          }, delay);
        }
      }
    }

    debounce.onclick = test2(handleClick, 2000, true);
  </script>
</body>

</html>